/*
 * $LastChangedDate: 2007-06-21 22:32:27 +0200 (Thu, 21 Jun 2007) $
 * $LastChangedRevision: 207 $
 * $LastChangedBy: david $
 */

#include "Log.h"
#include "Client.h"
#include "ClientSocket.h"
#include "SocketException.h"
#include "ServerErrorException.h"
#include <sstream>


/* Contador de clientes creados */
int Client::numClients = 0;

/* Contador de clientes ejecutando */
int Client::numExecuting = 0;

/* Numero de operaciones que realiza el cliente */
int Client::numOperations = 10;

extern bool end;
extern BlockingObject clients_executing;

Client::Client(std::string path, std::string mode) : Thread()
{
	fpath = path;
	fmode = mode;
	id = "(Cliente";
	std::stringstream ss;
	std::string str;
	ss << numClients++;
	ss >> str;
	id.append(str);
	id.append(", " + fpath + ", " + fmode + ")");
	Log::println(*this, "creado cliente que hará una petición en modo " +
			fmode + " por el fichero " + fpath, Log::INIT);
}

std::ostream & operator<<(std::ostream & os, const Client & c)
{
	os << c.id;
}

/* Detecta si la respuesta recibida del servidor es un error y en caso
 * afirmativo lanza una excepción. */
void Client::parseAnswer(std::string s) throw(ServerErrorException &)
{
	Log::println(*this, "Client: Respuesta recibida: " + s, Log::CONNECTION);
	if (!s.compare(0, 5, "ERROR")) {
		throw ServerErrorException();
	}
}

Client::~Client()
{
	/* nop */
}

/* Se conecta con el servidor y realiza una petición de lectura o escritura. */
void Client::run()
{
	clients_executing.synchronize_start();
	numExecuting++;
	clients_executing.synchronize_end();

	ClientSocket socket("localhost", 4242);
	Log::println(*this, "conectado al servidor", Log::CONNECTION);
	std::string input;

	try {
		try {
			Log::println(*this, "Enviando petición para el fichero " +
					fpath, Log::CONNECTION);
			socket << fpath;
			socket >> input;
			parseAnswer(input);

			Log::println(*this, "Enviando petición para abrir el fichero "
					+ fpath + " en modo " + fmode,
					Log::CONNECTION);
			socket << fmode;
			socket >> input;
			parseAnswer(input);

			int i;
			if (fmode == "r") {
				while (i < numOperations && !end) {
					std::stringstream ss;
					ss << "READ:" << i; /* i es el seek */
					socket << ss.str();
					socket >> input;
					parseAnswer(input);
					i++;
				}
			} else {
				while (i < numOperations && !end) {
					std::stringstream ss;
					ss << "WRITE:" << i /* La i es el seek */ 
						<< ":" << "DATA" << i;
					socket << ss.str();
					socket >> input;
					parseAnswer(input);
					i++;
				}
			}

			Log::println(*this,
				"Enviando petición para cerrar el fichero",
				Log::CONNECTION);
			socket << "CLOSE";
			socket >> input;
			parseAnswer(input);
			
			if (end) {
				synchronize_start();
				if (numExecuting == 1) {
					clients_executing.notifyAll();
				}
				synchronize_end();
				Log::println(*this, "Cliente terminando...");
			}
		}
		catch (SocketException&) { }
	} catch (SocketException& e) {
		Log::println(*this, "Exception: " + e.description());
	} catch (ServerErrorException & e) {
		Log::println(*this, "Error en el servidor");
		Log::println(*this,
			"Enviando petición para cerrar el fichero",
			Log::CONNECTION);
		socket << "CLOSE";
		socket >> input;
		parseAnswer(input);
	}
	clients_executing.synchronize_start();
	numExecuting--;
	clients_executing.synchronize_end();
}
